package com.ddxz.tool.sql.parse.impl;

import com.alibaba.druid.sql.ast.SQLExpr;
import com.alibaba.druid.sql.ast.SQLOrderBy;
import com.alibaba.druid.sql.ast.expr.*;
import com.alibaba.druid.sql.ast.statement.*;
import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleSizeExpr;
import com.ddxz.tool.core.convert.CollectionUtil;
import com.ddxz.tool.core.convert.StringUtil;
import com.ddxz.tool.sql.exception.DdxzSqlException;
import com.ddxz.tool.sql.parse.SqlParse;
import com.ddxz.tool.sql.parse.struct.*;
import com.ddxz.tool.sql.struct.DdxzSortType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import static com.ddxz.tool.sql.exception.SqlErrorStatus.*;

/**
 * @description
 * @author 朱宗刚
 * @date 2019年10月23日
 * @version 1.0.0
 */

public abstract class BaseSqlParse implements SqlParse {
    protected BaseSqlParse() {}

    @Override
    public void initialize(String sql) {
        // 根据SQL语句生成对应的语法树
        SQLSelectQuery query = initSql(sql);
        if (null == query) {
            throw new DdxzSqlException(INVALID_SQL, sql);
        }
        // 清空之前的结果
        clean();
        // 遍历语法树，从中提取相关的信息
        List<TableInfo> sourceList = accept(query, null);
        // 将相关信息进行下钻，从逻辑表转化为真实表
        nestParse(sourceList);
    }

    private void clean() {
        realTables.clear();
        selectList.clear();
        conditionList.clear();
        orderList.clear();
        args.clear();
        alias.clear();
    }

    private void nestParse(List<TableInfo> sourceList) {
        selectList.parallelStream().forEach(item -> {
            item.getMapping().forEach((key, val) -> {
                val.parallelStream().forEach(elem -> {
                    getRealName(sourceList, elem);
                });
            });
        });

        conditionList.parallelStream().forEach(item -> {
            getRealName(sourceList, item.getColumn());
        });

        orderList.parallelStream().forEach(item -> {
            getRealName(sourceList, item.getColumn());
        });

        sourceList.forEach(item -> {
            item.getColumnList().clear();
            item.getConditionList().clear();
            item.getOrderList().clear();
            item.getSubQuery().clear();
        });
    }

    private void getRealName(List<TableInfo> sourceList, ColumnInfo col) {
        if(col.getEnName() == null || col.getConstant()) {
            // 常量列无需查找所属表结构
            return;
        }

        boolean realTable;
        
        for (TableInfo table : sourceList) {
            if(CollectionUtil.isEmpty(table.getColumnList())) {
                continue;
            }
            
            for(ColumnInfo ci : table.getColumnList()) {
                realTable = col.getEnName().equals(ci.getEnName()) &&( col.getOwner().equals(table.getAlisa()) || col.getOwner().equals(table.getEnName()));
                if(realTable) {
                    if(CollectionUtil.isEmpty(table.getSubQuery())) {
                        col.setOwner(StringUtil.isEmpty(table.getSchema()) ? table.getEnName() : table.getSchema() + "." + table.getEnName());
                    }
                    continue;
                }
                if(col.getEnName().equals(ci.getAlias()) || col.getEnName().equals(ci.getEnName())) {
                    col.setEnName(CollectionUtils.isEmpty(table.getSubQuery()) || ci.getAlias() == null
                            ? ci.getEnName() : ci.getAlias());
                    col.setOwner(CollectionUtils.isEmpty(table.getSubQuery())
                            ? StringUtil.isEmpty(table.getSchema()) ? table.getEnName() : table.getSchema() + "." + table.getEnName()
                            : table.getAlisa() == null ? table.getEnName() : table.getAlisa());
                    break;
                }
            }
            
            if(!CollectionUtils.isEmpty(table.getSubQuery())) {
                getRealName(table.getSubQuery(), col);
            }
        }
    }

    private List<TableInfo> accept(SQLSelectQuery query, TableInfo info) {
        List<TableInfo> result = new ArrayList<>();
        if (query instanceof SQLSelectQueryBlock) {
            SQLSelectQueryBlock block = (SQLSelectQueryBlock) query;
            result.addAll(accept(block.getFrom(), info));
            accept(block.getSelectList(), result, info == null);
            accept(block.getWhere(), result);
            if(block.getGroupBy() != null && block.getGroupBy().getHaving() != null) {
                accept(block.getGroupBy().getHaving(), result);
            }
            accept(block.getOrderBy(), result);
        } else {
            SQLUnionQuery union = (SQLUnionQuery) query;
            result.addAll(accept(union.getLeft(), info));
            result.addAll(accept(union.getRight(), info));
        }

        System.out.println(result);
        return result;
    }
    
    private void accept0(SQLSelectQuery query, List<TableInfo> tableList) {
        if(query instanceof SQLSelectQueryBlock) {
            SQLSelectQueryBlock block = (SQLSelectQueryBlock) query;
            tableList.addAll(accept(block.getFrom(), null));
            accept(block.getSelectList(), tableList, false);
            accept(block.getWhere(), tableList);
            accept(block.getOrderBy(), tableList);
        } else {
            SQLUnionQuery union = (SQLUnionQuery) query;
            accept0(union.getLeft(), tableList);
            accept0(union.getRight(), tableList);
        }
    }

    private List<TableInfo> accept(SQLTableSource sts, TableInfo outter) {
        logger.info("begin: {}", outter);
        List<TableInfo> result;
        List<TableInfo> tmp;
        if (null == outter) {
            result = new ArrayList<>();
        } else {
            result = outter.getSubQuery();
        }

        TableInfo ti = new TableInfo();

        if (sts instanceof SQLExprTableSource) {
            result.add(getTableInfo((SQLExprTableSource) sts));
        } else if (sts instanceof SQLJoinTableSource) {
            result.addAll(accept(((SQLJoinTableSource) sts).getLeft(), outter));
            result.addAll(accept(((SQLJoinTableSource) sts).getRight(), outter));
            accept(((SQLJoinTableSource)sts).getCondition(), result);
        } else if (sts instanceof SQLSubqueryTableSource) {
            if (null == sts.getAlias()) {
                throw new DdxzSqlException(SUBQUERY_NAME);
            }

            ti.setAlisa(sts.getAlias());
            tmp = accept(((SQLSubqueryTableSource) sts).getSelect().getQuery(), ti);
            ti.setSubQuery(tmp);
            result.add(ti);
        } else if (sts instanceof SQLUnionQuery) {
            if (null == sts.getAlias()) {
                throw new DdxzSqlException(SUBQUERY_NAME);
            }

            ti.setAlisa(sts.getAlias());
            tmp = accept(sts, ti);
            ti.setSubQuery(tmp);
            result.add(ti);
        } else {
            logger.error(UNSUPPORT.getMessage(), sts.getClass().getName());
            throw new DdxzSqlException(UNSUPPORT, sts.getClass().getName());
        }

        logger.debug("end: {}", result);
        return result;
    }

    private TableInfo getTableInfo(SQLExprTableSource sts) {
        TableInfo ti = new TableInfo();
        ti.setEnName(trimName(sts.getName().getSimpleName()));
        ti.setAlisa(trimName(sts.getAlias()));
        if (sts.getExpr() instanceof SQLPropertyExpr) {
            ti.setSchema(trimName(((SQLPropertyExpr) sts.getExpr()).getOwnernName()));
        } else {
            ti.setSchema(getDefaultSchema());
        }

        if (ti.getSchema() == null) {
            throw new DdxzSqlException(SCHEMA_ERROR);
        }

        realTables.add(ti);

        return ti;
    }

    private int getTableIndex(List<TableInfo> tableList, String alias) {
        if(StringUtil.isEmpty(alias)) {
            if( tableList.size() > 1) {
                throw new DdxzSqlException(UNCENTER_COLUMN);
            } else {
                return 0;
            }
        }

        int idx = 0;
        for (TableInfo table : tableList) {
            if (alias.equals(table.getAlisa())) {
                return idx;
            } else if(alias.equals(table.getEnName())) {
                return idx;
            } else {
                ++idx;
            }
        }

        return idx >= tableList.size() ? -1 : idx;
    }

    private void accept(List<SQLSelectItem> itemList, List<TableInfo> tableList, boolean isTop) {
        if (CollectionUtils.isEmpty(tableList)) {
            throw new DdxzSqlException(NO_TABLE_FOUND);
        }

        if (CollectionUtils.isEmpty(itemList)) {
            throw new DdxzSqlException(NO_RESULT_FOUND);
        }

        InOutMapping selectItem;
        for (SQLSelectItem item : itemList) {
            selectItem = new InOutMapping();

            if (item.getExpr() instanceof SQLAggregateExpr) {
                SQLAggregateExpr expr = (SQLAggregateExpr) item.getExpr();
                accept(expr, item.getAlias(), expr.getMethodName(), selectItem, tableList);
            } else if (item.getExpr() instanceof SQLMethodInvokeExpr) {
                SQLMethodInvokeExpr expr = (SQLMethodInvokeExpr) item.getExpr();
                accept(expr, item.getAlias(), expr.getMethodName(), selectItem, tableList);
            } else if (item.getExpr() instanceof SQLIntegerExpr || item.getExpr() instanceof SQLCharExpr || item.getExpr() instanceof OracleSizeExpr) {
                if (null == item.getAlias()) {
                    continue;
                } else {
                    selectItem.getMapping().put(trimName(item.getAlias()), new ArrayList<>());
                    ColumnInfo colInfo = new ColumnInfo(ColumnInfo.SELECT);
                    colInfo.setConstant(true);
                    selectItem.getMapping().get(trimName(item.getAlias())).add(colInfo);
                }
            } else if (item.getExpr() instanceof SQLIdentifierExpr) {
                SQLIdentifierExpr expr = (SQLIdentifierExpr)item.getExpr();
                accept(expr, item.getAlias(), expr.getName(), selectItem, tableList);
            } else if (item.getExpr() instanceof SQLPropertyExpr) {
                SQLPropertyExpr expr = (SQLPropertyExpr)item.getExpr();
                accept(expr, item.getAlias(), expr.getName(), selectItem, tableList);
            } else if (item.getExpr() instanceof SQLBinaryOpExpr) {
                SQLBinaryOpExpr opExpr = (SQLBinaryOpExpr)item.getExpr();
                accept(opExpr, item.getAlias(), opExpr.getOperator().name(), selectItem, tableList);
            } else if (item.getExpr() instanceof SQLCaseExpr) {
                accept(item.getExpr(), item.getAlias(), "case", selectItem, tableList);
            } else {
                logger.error(UNCENTER_COLUMN.getMessage(), item.getExpr().getClass().getName());
                throw new DdxzSqlException(UNCENTER_COLUMN, item.getExpr().getClass().getName());
            }

            if (isTop) {
                selectList.add(selectItem);
            }
        }
    }

    private void accept(SQLExpr expr, String as, String name, InOutMapping selectItem, List<TableInfo> tableList) {
        String tmp = StringUtil.isEmpty(as) ? name : as;
        if(alias.contains(tmp)) {
            throw new DdxzSqlException(REPEAT_ALIAS, name);
        }

        alias.add(tmp);

        selectItem.getMapping().put(trimName(tmp), getColumnInfo(expr, tableList, ColumnInfo.SELECT, null));
    }

    private List<ColumnInfo> getMethodArgument(List<SQLExpr> args, List<TableInfo> tableList, int position) {
        List<ColumnInfo> result = new ArrayList<>();

        for (SQLExpr expr : args) {
            result.addAll(getColumnInfo(expr, tableList, position, null));
        }

        return result;
    }

    private List<ColumnInfo> getColumnInfo(SQLExpr expr, List<TableInfo> tableList, int position, String alias) {
        List<ColumnInfo> result = new ArrayList<>();
        ColumnInfo columnInfo = new ColumnInfo(position);
        columnInfo.setAlias(trimName(alias));
        int idx;
        String name;
        if (expr instanceof SQLIdentifierExpr) {
            name = ((SQLIdentifierExpr) expr).getName();
            columnInfo.setEnName(trimName(name));
            boolean flag = name.charAt(0) == VAR_FLAG || name.startsWith(VAR_QUALIT);
            if(flag) {
                columnInfo.setConstant(true);
                result.add(columnInfo);
                return result;
            }

            if(tableList.size() > 1) {
                logger.error(UNCENTER_COLUMN.getMessage(), name);
                throw new DdxzSqlException(UNCENTER_COLUMN, name);
            } else {
                columnInfo.setOwner(StringUtil.isEmpty(tableList.get(0).getAlisa()) ? tableList.get(0).getEnName() : tableList.get(0).getAlisa());
                tableList.get(0).getColumnList().add(columnInfo);
            }
            result.add(columnInfo);
            return result;
        } else if (expr instanceof SQLPropertyExpr) {
            columnInfo.setEnName(trimName(((SQLPropertyExpr) expr).getName()));
            idx = getTableIndex(tableList, trimName(((SQLPropertyExpr) expr).getOwnernName()));
            if (idx == -1) {
                logger.error(UNCENTER_COLUMN.getMessage(), columnInfo.getEnName());
                throw new DdxzSqlException(UNCENTER_COLUMN);
            } else {
                if (null == tableList.get(idx).getAlisa()) {
                    columnInfo.setOwner(tableList.get(idx).getEnName());
                } else {
                    columnInfo.setOwner(tableList.get(idx).getAlisa());
                }
                tableList.get(idx).getColumnList().add(columnInfo);
                result.add(columnInfo);
            }
            return result;
        } else if (expr instanceof SQLBinaryOpExpr) {
            result.addAll(getColumnInfo(((SQLBinaryOpExpr) expr).getLeft(), tableList, position, null));
            result.addAll(getColumnInfo(((SQLBinaryOpExpr) expr).getRight(), tableList, position, null));
        } else if (expr instanceof SQLIntegerExpr || expr instanceof SQLCharExpr) {
            columnInfo.setEnName(trimName(expr.toString()));
            columnInfo.setConstant(true);
            result.add(columnInfo);
        } else if (expr instanceof SQLCaseExpr) {
            if (((SQLCaseExpr) expr).getValueExpr() != null) {
                getColumnInfo(((SQLCaseExpr) expr).getValueExpr(), tableList, position, null);
            } else {
                getColumnInfo(((SQLBinaryOpExpr) ((SQLCaseExpr) expr).getItems().get(0).getConditionExpr()).getLeft(),
                        tableList, position, null);
            }
        } else if(expr instanceof SQLNullExpr) {
            columnInfo.setEnName("null");
            result.add(columnInfo);
        } else if(expr instanceof SQLMethodInvokeExpr) {
            result.addAll(getMethodArgument(((SQLMethodInvokeExpr)expr).getArguments(), tableList, position));
        } else if(expr instanceof SQLAggregateExpr) {
            result.addAll(getMethodArgument(((SQLAggregateExpr)expr).getArguments(), tableList, position));
        } else {
            logger.error(UNSUPPORT.getMessage(), expr.getClass().getName());
            throw new DdxzSqlException(UNSUPPORT, expr.getClass().getName());
        }

        return result;
    }

    private void accept(SQLExpr expr, List<TableInfo> tableList) {
        if(null == expr || CollectionUtils.isEmpty(tableList)) {
            return;
        }
        
        if(expr instanceof SQLBinaryOpExpr) {
            getBinaryCondition((SQLBinaryOpExpr)expr, tableList);
        } else if (expr instanceof SQLBetweenExpr) {
            getBetweenCondition((SQLBetweenExpr)expr, tableList);
        } else if (expr instanceof SQLInListExpr) {
            getInListCondition((SQLInListExpr)expr, tableList);
        } else if (expr instanceof SQLExistsExpr) {
            getExistsCondition((SQLExistsExpr)expr, tableList);
        } else if (expr instanceof SQLInSubQueryExpr){
            getInSubQuery((SQLInSubQueryExpr) expr, tableList);
        }  else {
            logger.error(UNSUPPORT.getMessage(), expr.getClass().getName());
            throw new DdxzSqlException(UNSUPPORT, expr.getClass().getName());
        }
    }

    private void getInSubQuery(SQLInSubQueryExpr expr, List<TableInfo> tableList) {
        ConditionInfo conditionInfo = new ConditionInfo();
        List<ColumnInfo> cols = getColumnInfo(expr.getExpr(), tableList, ColumnInfo.CONDITION, null);
        conditionInfo.setColumn(cols.get(0));
        conditionInfo.getLeft().addAll(cols);
        conditionInfo.setOperator("IN");
        conditionInfo.setRight(null);
        conditionList.add(conditionInfo);
    }
    
    private void getBinaryCondition(SQLBinaryOpExpr expr, List<TableInfo> tableList) {
        switch (expr.getOperator()) {
            case PG_And:
            case BooleanAnd:
            case BooleanOr:
                accept(expr.getLeft(), tableList);
                accept(expr.getRight(), tableList);
                return;
            default:
                getBinaryCondition(expr.getLeft(), expr.getRight(), expr.getOperator(), tableList);
                break;
        }
    }

    private void getBinaryCondition(SQLExpr left, SQLExpr right, SQLBinaryOperator operator, List<TableInfo> tableList) {
        List<ColumnInfo> cols;
        ConditionInfo condition = new ConditionInfo();
        condition.setOperator(operator.name());
        boolean flag = left instanceof SQLIdentifierExpr || left instanceof SQLPropertyExpr
                || left instanceof SQLIntegerExpr || left instanceof SQLCharExpr || left instanceof OracleSizeExpr;
        if(flag) {
            cols = getColumnInfo(left, tableList, ColumnInfo.CONDITION, null);
            flag = VAR_FLAG == cols.get(0).getEnName().charAt(0) ||
                    cols.get(0).getEnName().startsWith(VAR_QUALIT);
            if(flag) {
                args.add(cols.get(0).getEnName().substring(1));
            }
        } else if(left instanceof SQLMethodInvokeExpr) {
            // 左侧是系统或自定义函数
            cols = getMethodArgument(((SQLMethodInvokeExpr)left).getArguments(), tableList, ColumnInfo.CONDITION);
        } else if(left instanceof SQLAggregateExpr) {
            // 左侧是聚集函数
            cols = getMethodArgument(((SQLAggregateExpr)left).getArguments(), tableList, ColumnInfo.CONDITION);
        } else {
            logger.error(UNSUPPORT.getMessage(), left.toString());
            throw new DdxzSqlException(UNSUPPORT, left.toString());
        }
        condition.getLeft().addAll(cols);
        condition.setColumn(cols.get(0));

        flag = right instanceof SQLIdentifierExpr || right instanceof SQLPropertyExpr
                || right instanceof SQLIntegerExpr || right instanceof SQLCharExpr || right instanceof OracleSizeExpr;
        if(flag) {
            cols = getColumnInfo(right, tableList, ColumnInfo.CONDITION, null);
            flag = VAR_FLAG == cols.get(0).getEnName().charAt(0) ||
                    cols.get(0).getEnName().startsWith(VAR_QUALIT);
            if(flag) {
                args.add(cols.get(0).getEnName().substring(1));
            }
        } else if(right instanceof SQLMethodInvokeExpr) {
            // 左侧是系统或自定义函数
            cols = getMethodArgument(((SQLMethodInvokeExpr)right).getArguments(), tableList, ColumnInfo.CONDITION);
        } else if(left instanceof SQLAggregateExpr) {
            // 左侧是聚集函数
            cols = getMethodArgument(((SQLAggregateExpr)right).getArguments(), tableList, ColumnInfo.CONDITION);
        } else {
            logger.error(UNSUPPORT.getMessage(), right.toString());
            throw new DdxzSqlException(UNSUPPORT, right.toString());
        }
        condition.getRight().add(cols);
        condition.getValues().addAll(cols);

        conditionList.add(condition);
    }

    private void getBetweenCondition(SQLBetweenExpr expr, List<TableInfo> tableList) {
        List<ColumnInfo> cols = getColumnInfo(expr.getTestExpr(), tableList, ColumnInfo.CONDITION, null);
        
        ConditionInfo conditionInfo = new ConditionInfo();
        conditionInfo.setColumn(cols.get(0));
        conditionInfo.getLeft().addAll(cols);
        conditionInfo.setOperator("BETWEEN");
        cols = getColumnInfo(expr.getBeginExpr(), tableList, ColumnInfo.CONDITION, null);
        conditionInfo.getValues().add(cols.get(0).getEnName());
        conditionInfo.getRight().add(cols);
        boolean flag = VAR_FLAG == cols.get(0).getEnName().charAt(0) ||
                cols.get(0).getEnName().startsWith(VAR_QUALIT);
        if(flag) {
            args.add(cols.get(0).getEnName().substring(1));
        }
        cols = getColumnInfo(expr.getEndExpr(), tableList, ColumnInfo.CONDITION, null);
        conditionInfo.getRight().add(cols);
        flag = VAR_FLAG == cols.get(0).getEnName().charAt(0) ||
                cols.get(0).getEnName().startsWith(VAR_QUALIT);
        if(flag) {
            args.add(cols.get(0).getEnName().substring(1));
        }
        conditionInfo.getValues().add(cols.get(0).getEnName());
        
        int idx = getTableIndex(tableList, conditionInfo.getColumn().getOwner());
        tableList.get(idx).getConditionList().add(conditionInfo);
        conditionList.add(conditionInfo);
    }
    
    private void getInListCondition(SQLInListExpr expr, List<TableInfo> tableList) {
        ConditionInfo conditionInfo = new ConditionInfo();
        List<ColumnInfo> cols = getColumnInfo(expr.getExpr(), tableList, ColumnInfo.CONDITION, null);
        conditionInfo.setColumn(cols.get(0));
        conditionInfo.getLeft().addAll(cols);
        conditionInfo.setOperator("IN");
        cols = getMethodArgument(expr.getTargetList(), tableList, ColumnInfo.CONDITION);
        conditionInfo.getRight().addAll(cols);
        for(ColumnInfo col : cols) {
            conditionInfo.getValues().add(col.getEnName());
            boolean flag = VAR_FLAG == cols.get(0).getEnName().charAt(0) ||
                    cols.get(0).getEnName().startsWith(VAR_QUALIT);
            if(flag) {
                args.add(col.getEnName().substring(1));
            }
        }
        
        int idx = getTableIndex(tableList, conditionInfo.getColumn().getOwner());
        tableList.get(idx).getConditionList().add(conditionInfo);
        conditionList.add(conditionInfo);
    }
    
    private void getExistsCondition(SQLExistsExpr expr, List<TableInfo> tableList) {
        accept0(expr.getSubQuery().getQuery(), tableList);
    }

    private void accept(SQLOrderBy order, List<TableInfo> tableList) {
        if(null == order || CollectionUtils.isEmpty(order.getItems())) {
            return;
        }
        
        OrderInfo orderInfo;
        List<ColumnInfo> cols;
        for(SQLSelectOrderByItem item : order.getItems()) {
            orderInfo = new OrderInfo();
            cols = getColumnInfo(item.getExpr(), tableList, ColumnInfo.ORDER, null);
            if(cols.size() != 1) {
                logger.error("ORDER: {}", cols);
                throw new DdxzSqlException(INVALID_ORDER);
            }
            
            orderInfo.setColumn(cols.get(0));
            orderInfo.setType(item.getType() == null ? DdxzSortType.DESC.getValue() : item.getType().name());
            orderList.add(orderInfo);
        }
    }
    
    private String trimName(String name) {
        if (StringUtil.isEmpty(name)) {
            return name;
        }

        String tmp = StringUtil.trim(name, getLeftQuote().charAt(0));
        tmp = StringUtil.trim(tmp, '\'');
        return StringUtil.trim(tmp, getRightQuote().charAt(0)).trim();
    }

    @Override
    public List<InOutMapping> getSelectList() {
        return selectList;
    }

    @Override
    public List<TableInfo> getFrom() {
        return realTables;
    }

    @Override
    public String getArgs() {
        return StringUtil.join(args);
    }

    @Override
    public List<OrderInfo> getOrderBy() {
        return orderList;
    }

    @Override
    public List<ConditionInfo> getWhere() {
        return conditionList;
    }

    private List<InOutMapping> selectList = new ArrayList<>();
    
    private List<OrderInfo> orderList = new ArrayList<>();
    
    private List<ConditionInfo> conditionList = new ArrayList<>();

    private List<TableInfo> realTables = new ArrayList<>();

    private List<String> args = new ArrayList<>();

    private Set<String> alias = new HashSet<>();

    private static final Logger logger = LoggerFactory.getLogger(BaseSqlParse.class);

    private static final char VAR_FLAG = '$';
    private static final String VAR_QUALIT = "\'$";

    public abstract String getLeftQuote();

    public abstract String getRightQuote();

    public abstract String getDefaultSchema();

    public abstract SQLSelectQuery initSql(String sql);
}
